package societyGames.document.model
{
import flash.events.EventDispatcher;

import mx.collections.ArrayCollection;

public class CommandStack extends EventDispatcher
{
  private var _doneCommands:ArrayCollection = new ArrayCollection(); //ICommand
  private var _undoneCommands:ArrayCollection = new ArrayCollection(); //ICommand
  private var _savedCommand:ICommand;

  //Clear everything out.
  public function clear():void
  {
    this._doneCommands.removeAll();
    this._undoneCommands.removeAll();
    this._savedCommand = null;
  }

  [Bindable]
  public function get doneCommands():ArrayCollection
  {
    return this._doneCommands;
  }

  public function set doneCommands(value:ArrayCollection):void
  {
    //just to suppress warnings.
  }

  [Bindable]
  public function get undoneCommands():ArrayCollection
  {
    return this._undoneCommands;
  }

  public function set undoneCommands(value:ArrayCollection):void
  {
    //just to suppress warnings.
  }

  //Description of the last done command. May be null.
  public function get lastDoneDescription():String
  {
    if (this._doneCommands.length > 0)
    {
      return this._doneCommands[this._doneCommands.length - 1].description;
    }
    return null;
  }

  //Description of the last undone command. May be null.
  public function get lastUndoneDescription():String
  {
    if (this._undoneCommands.length > 0)
    {
      return this._undoneCommands[this._undoneCommands.length - 1].description;
    }
    return null;
  }

  //Do a particular command.
  public function doCommand(command:ICommand):void
  {
    //Wipe out the previously undone commands. They can no longer be redone.
    this._undoneCommands.removeAll();
    //Do the newest command.
    command.doWork();
    //Add it to the done commands.
    this._doneCommands.addItem(command);
  }

  //Undo the last command done.
  public function undoCommand():void
  {
    if (this._doneCommands.length > 0)
    {
      //Get last done command.
      var command:ICommand = ICommand(this._doneCommands.removeItemAt(this._doneCommands.length - 1));
      //Undo it.
      command.undoWork();
      //Add it to the undone commands.
      this._undoneCommands.addItem(command);
    }
    else
    {
      throw new Error("No commands to undo.");
    }
  }

  //Redo the last command undone.
  public function redoCommand():void
  {
    if (this._undoneCommands.length > 0)
    {
      //Take the last undone command.
      var command:ICommand = ICommand(this._undoneCommands.removeItemAt(this._undoneCommands.length - 1));
      //And do it again.
      command.doWork();
      //Add to done.
      this._doneCommands.addItem(command);
    }
    else
    {
      throw new Error("No commands to redo.");
    }
  }

  //Document has been saved. Change isSaved state.
  public function saved():void
  {
    if (this._doneCommands.length > 0)
    {
      this._savedCommand = ICommand(this._doneCommands.getItemAt(this._doneCommands.length - 1));
    }
    else
    {
      this._savedCommand = null;
    }
  }

  //Whether the current state has been saved.
  public function get isSaved():Boolean
  {
    if (this._doneCommands.length > 0) //We have a last done command.
    {
      return this._savedCommand == this._doneCommands.getItemAt(this._doneCommands.length - 1);
    }
    else
    {
      return this._savedCommand == null;
    }
  }
}
}